package sbt.metamodel;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * I represent an Obsel.
 * <p>
 * NB: I can not be constructed directly. Use
 * {@link PrimaryTrace#createObsel(String, long, long, String, Map, Map, Map)}
 * or
 * {@link TransformationMethod#createObsel(String, long, long, String, Map, Map, Map)}
 * instead.
 * @author pa
 *
 */
public class Obsel {
    private AbstractTrace owner;
    private long id;
    private String type, subject;
    private long begin, end;
    private Map<String, Object> attributes;
    private Map<String, Obsel> related, relating;
    
    Obsel(AbstractTrace owner, long id, String type, long begin, long end,
    		String subject, Map<String, Object> attributes,
    		Map<String, Obsel> related, Map<String, Obsel> relating) {
    	this.id = id;
        this.owner = owner;
        this.type = type;
        this.begin = begin;
        this.end = end;
        this.subject = subject;
        this.attributes = new HashMap<String, Object>();
        if (attributes != null) {
        	this.attributes.putAll(attributes);
        }
        this.related = new HashMap<String, Obsel>();
        if (related != null) {
        	for (String reltyp: related.keySet()) {
        		Obsel other = related.get(reltyp);
        		if (other.owner != owner) {
        			throw new MetamodelException();
        		}
        		this.related.put(reltyp, other);
        		other.relating.put(reltyp, this);
        	}
        }
        this.relating = new HashMap<String, Obsel>();
        if (relating != null) {
        	for (String reltyp: relating.keySet()) {
        		Obsel other = relating.get(reltyp);
        		if (other.owner != owner) {
        			throw new MetamodelException();
        		}
        		this.relating.put(reltyp, other);
        		other.related.put(reltyp, this);
        	}
        }
    }

    /**
     * The trace to which this obsel belongs.
     * @return  the trace owning this Obsel
     */
    public AbstractTrace getOwner() {
        return owner;
    }

    /**
     * The internal identifier of the obsel.
     * <p>
     * Note that identifiers are ordered according to creation time; it is
     * therefore easy to find the latest obsels.
     * 
     * @return  the internal identifier
     * 
     * @see AbstractTrace#getObselsById()
     */
    public long getId() {
    	return id;
    }
    
    /**
     * The type URI of this Obsel (relative to the owner's trace model).
     * @return  the type URI (as stored)
     */
    public String getType() {
    	return type;
    }
    
    /**
     * The subject of this Obsel.
     * @return  the subject
     */
    public String getSubject() {
    	return subject;
    }
    
    /**
     * The begin timestamp of this Obsel (in ms since owner's origin).
     * @return  the begin timestamp
     */
    public long getBegin() {
    	return begin;
    }
    
    /**
     * The end timestamp of this Obsel (in ms since owner's origin).
     * @return  the begin timestamp
     */
    public long getEnd() {
    	return end;
    }
    
    /**
     * The attributes of this obsel.
     * <p>
     * Keys in the returned map are attribute URI, relative to the owner's
     * trace model).
     * 
     * @return  the attributes as a map
     */
    public Map<String, Object> getAttributes() {
    	return Collections.unmodifiableMap(attributes);
    }

    /**
     * The outgoing relations of this obsel.
     * <p>
     * Keys in the returned map are relation URI, relative to the owner's
     * trace model).
     * 
     * @return  the attributes as a map
     */
    public Map<String, Obsel> getRelated() {
    	return Collections.unmodifiableMap(related);
    }

    /**
     * The incoming relations of this obsel.
     * <p>
     * Keys in the returned map are relation URI, relative to the owner's
     * trace model).
     * 
     * @return  the attributes as a map
     */
    public Map<String, Obsel> getRelating() {
    	return Collections.unmodifiableMap(relating);
    }
}
